import asyncio
import smtplib
import ssl
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from typing import Dict, Any, List, Optional, Union
from datetime import datetime, timedelta
from enum import Enum
import uuid
import json
from dataclasses import dataclass, asdict

from ..models.audit_models import AuditResult, AuditDecision
from ..utils.logger import get_logger

logger = get_logger(__name__)


class NotificationStatus(str, Enum):
    """通知状态枚举."""
    PENDING = "pending"
    SENT = "sent"
    FAILED = "failed"
    RETRY = "retry"


class NotificationType(str, Enum):
    """通知类型枚举."""
    EMAIL = "email"
    WEBHOOK = "webhook"
    SMS = "sms"


@dataclass
class NotificationRecord:
    """通知记录."""
    notification_id: str
    request_id: str
    recipient: str
    notification_type: NotificationType
    template_name: str
    content: Dict[str, Any]
    status: NotificationStatus
    attempts: int = 0
    max_attempts: int = 3
    created_at: datetime = None
    sent_at: Optional[datetime] = None
    error_message: Optional[str] = None
    
    def __post_init__(self):
        if self.created_at is None:
            self.created_at = datetime.now()


class EmailTemplate:
    """邮件模板."""
    
    def __init__(self, name: str, subject: str, html_body: str, text_body: Optional[str] = None):
        self.name = name
        self.subject = subject
        self.html_body = html_body
        self.text_body = text_body or self._html_to_text(html_body)
    
    def _html_to_text(self, html: str) -> str:
        """简单的HTML转文本（生产环境应使用专业库）."""
        import re
        # 移除HTML标签
        text = re.sub(r'<[^>]+>', '', html)
        # 清理多余空白
        text = re.sub(r'\s+', ' ', text).strip()
        return text
    
    def render(self, context: Dict[str, Any]) -> Dict[str, str]:
        """渲染模板."""
        subject = self.subject.format(**context)
        html_body = self.html_body.format(**context)
        text_body = self.text_body.format(**context)
        
        return {
            "subject": subject,
            "html_body": html_body,
            "text_body": text_body
        }


class NotificationManager:
    """通知管理器."""
    
    def __init__(self):
        # 通知记录（生产环境应使用数据库）
        self.notification_records: List[NotificationRecord] = []
        
        # 邮件配置（应从环境变量读取）
        self.smtp_config = {
            "host": "smtp.gmail.com",
            "port": 587,
            "username": "",  # 配置邮箱用户名
            "password": "",  # 配置邮箱密码
            "use_tls": True
        }
        
        # 邮件模板
        self.email_templates = self._load_email_templates()
        
        # 重试队列
        self.retry_queue: List[str] = []
        
        logger.info("通知管理器初始化完成")
    
    def _load_email_templates(self) -> Dict[str, EmailTemplate]:
        """加载邮件模板."""
        templates = {}
        
        # 审核通过模板
        templates["audit_approved"] = EmailTemplate(
            name="audit_approved",
            subject="教室申请审核通过 - {classroom_id}",
            html_body="""
            <html>
            <body>
                <h2>审核结果通知</h2>
                <p>亲爱的 {applicant_name}：</p>
                <p>您的教室借用申请已通过审核！</p>
                
                <h3>申请详情：</h3>
                <ul>
                    <li><strong>教室：</strong>{classroom_id}</li>
                    <li><strong>时间：</strong>{start_time} - {end_time}</li>
                    <li><strong>用途：</strong>{usage_purpose}</li>
                    <li><strong>参与人数：</strong>{participants_count}人</li>
                    <li><strong>审核得分：</strong>{final_score}分</li>
                </ul>
                
                <h3>审核意见：</h3>
                <p>{approval_reason}</p>
                
                {conditions_html}
                
                <p>请按时使用教室，如有变更请提前联系管理员。</p>
                
                <p>祝好！<br>教室管理系统</p>
            </body>
            </html>
            """
        )
        
        # 审核拒绝模板
        templates["audit_rejected"] = EmailTemplate(
            name="audit_rejected",
            subject="教室申请未通过审核 - {classroom_id}",
            html_body="""
            <html>
            <body>
                <h2>审核结果通知</h2>
                <p>亲爱的 {applicant_name}：</p>
                <p>很抱歉，您的教室借用申请未能通过审核。</p>
                
                <h3>申请详情：</h3>
                <ul>
                    <li><strong>教室：</strong>{classroom_id}</li>
                    <li><strong>时间：</strong>{start_time} - {end_time}</li>
                    <li><strong>用途：</strong>{usage_purpose}</li>
                    <li><strong>审核得分：</strong>{final_score}分</li>
                </ul>
                
                <h3>拒绝原因：</h3>
                <ul>
                {reasons_html}
                </ul>
                
                {suggestions_html}
                
                <p>如有疑问，请联系管理员。欢迎修改申请后重新提交。</p>
                
                <p>祝好！<br>教室管理系统</p>
            </body>
            </html>
            """
        )
        
        # 需要补充材料模板
        templates["audit_conditional"] = EmailTemplate(
            name="audit_conditional",
            subject="教室申请需要补充材料 - {classroom_id}",
            html_body="""
            <html>
            <body>
                <h2>审核结果通知</h2>
                <p>亲爱的 {applicant_name}：</p>
                <p>您的教室借用申请需要补充材料或满足额外条件。</p>
                
                <h3>申请详情：</h3>
                <ul>
                    <li><strong>教室：</strong>{classroom_id}</li>
                    <li><strong>时间：</strong>{start_time} - {end_time}</li>
                    <li><strong>用途：</strong>{usage_purpose}</li>
                </ul>
                
                <h3>需要满足的条件：</h3>
                <ul>
                {conditions_html}
                </ul>
                
                <p>请尽快补充相关材料或满足条件，我们将重新审核您的申请。</p>
                
                <p>祝好！<br>教室管理系统</p>
            </body>
            </html>
            """
        )
        
        return templates
    
    async def send_audit_notification(self, result: AuditResult, 
                                    recipient_email: str,
                                    applicant_name: str = "用户") -> str:
        """发送审核结果通知."""
        
        # 选择模板
        template_name = self._get_template_name(result.decision)
        template = self.email_templates.get(template_name)
        
        if not template:
            raise ValueError(f"未找到模板: {template_name}")
        
        # 准备上下文数据
        context = self._prepare_notification_context(result, applicant_name)
        
        # 创建通知记录
        notification = NotificationRecord(
            notification_id=str(uuid.uuid4()),
            request_id=result.request_id,
            recipient=recipient_email,
            notification_type=NotificationType.EMAIL,
            template_name=template_name,
            content=context,
            status=NotificationStatus.PENDING
        )
        
        self.notification_records.append(notification)
        
        # 发送通知
        success = await self._send_email_notification(notification, template)
        
        if success:
            notification.status = NotificationStatus.SENT
            notification.sent_at = datetime.now()
            logger.info(f"审核通知发送成功: {result.request_id} -> {recipient_email}")
        else:
            notification.status = NotificationStatus.FAILED
            notification.attempts += 1
            self.retry_queue.append(notification.notification_id)
            logger.error(f"审核通知发送失败: {result.request_id} -> {recipient_email}")
        
        return notification.notification_id
    
    def _get_template_name(self, decision: AuditDecision) -> str:
        """根据审核决策获取模板名称."""
        template_map = {
            AuditDecision.APPROVED: "audit_approved",
            AuditDecision.REJECTED: "audit_rejected",
            AuditDecision.CONDITIONAL: "audit_conditional",
            AuditDecision.PENDING: "audit_conditional"  # 待审核也使用条件模板
        }
        return template_map.get(decision, "audit_conditional")
    
    def _prepare_notification_context(self, result: AuditResult, 
                                    applicant_name: str) -> Dict[str, Any]:
        """准备通知上下文数据."""
        
        # 格式化原因列表
        reasons_html = ""
        if result.reasons:
            reasons_html = "\n".join([f"<li>{reason}</li>" for reason in result.reasons])
        
        # 格式化条件列表
        conditions_html = ""
        if result.conditions:
            conditions_html = "<h3>需要满足的条件：</h3><ul>\n"
            conditions_html += "\n".join([f"<li>{condition}</li>" for condition in result.conditions])
            conditions_html += "\n</ul>"
        
        # 格式化建议列表
        suggestions_html = ""
        if result.suggestions:
            suggestions_html = "<h3>建议：</h3><ul>\n"
            suggestions_html += "\n".join([f"<li>{suggestion}</li>" for suggestion in result.suggestions])
            suggestions_html += "\n</ul>"
        
        return {
            "applicant_name": applicant_name,
            "classroom_id": "未知教室",  # 需要从请求数据获取
            "start_time": "未知时间",
            "end_time": "未知时间", 
            "usage_purpose": "未知用途",
            "participants_count": "未知",
            "final_score": f"{result.final_score:.1f}",
            "approval_reason": result.reasons[0] if result.reasons else "审核完成",
            "reasons_html": reasons_html,
            "conditions_html": conditions_html,
            "suggestions_html": suggestions_html,
            "request_id": result.request_id
        }
    
    async def _send_email_notification(self, notification: NotificationRecord,
                                     template: EmailTemplate) -> bool:
        """发送邮件通知."""
        try:
            # 检查SMTP配置
            if not self.smtp_config["username"] or not self.smtp_config["password"]:
                logger.warning("SMTP配置不完整，跳过邮件发送")
                return False
            
            # 渲染模板
            rendered = template.render(notification.content)
            
            # 创建邮件
            msg = MIMEMultipart("alternative")
            msg["Subject"] = rendered["subject"]
            msg["From"] = self.smtp_config["username"]
            msg["To"] = notification.recipient
            
            # 添加文本和HTML部分
            text_part = MIMEText(rendered["text_body"], "plain")
            html_part = MIMEText(rendered["html_body"], "html")
            
            msg.attach(text_part)
            msg.attach(html_part)
            
            # 发送邮件
            context = ssl.create_default_context()
            with smtplib.SMTP(self.smtp_config["host"], self.smtp_config["port"]) as server:
                if self.smtp_config["use_tls"]:
                    server.starttls(context=context)
                server.login(self.smtp_config["username"], self.smtp_config["password"])
                server.send_message(msg)
            
            return True
            
        except Exception as e:
            notification.error_message = str(e)
            logger.error(f"邮件发送异常: {str(e)}")
            return False
    
    async def send_webhook_notification(self, webhook_url: str, 
                                      result: AuditResult) -> str:
        """发送Webhook通知."""
        try:
            import httpx
            
            payload = {
                "event_type": "audit_completed",
                "request_id": result.request_id,
                "decision": result.decision.value,
                "final_score": result.final_score,
                "confidence_level": result.confidence_level,
                "reasons": result.reasons,
                "timestamp": datetime.now().isoformat()
            }
            
            notification = NotificationRecord(
                notification_id=str(uuid.uuid4()),
                request_id=result.request_id,
                recipient=webhook_url,
                notification_type=NotificationType.WEBHOOK,
                template_name="webhook_audit_result",
                content=payload,
                status=NotificationStatus.PENDING
            )
            
            self.notification_records.append(notification)
            
            # 发送请求
            async with httpx.AsyncClient() as client:
                response = await client.post(
                    webhook_url,
                    json=payload,
                    timeout=10.0,
                    headers={"Content-Type": "application/json"}
                )
                
                if response.status_code == 200:
                    notification.status = NotificationStatus.SENT
                    notification.sent_at = datetime.now()
                    logger.info(f"Webhook通知发送成功: {webhook_url}")
                    return notification.notification_id
                else:
                    raise Exception(f"HTTP {response.status_code}: {response.text}")
                    
        except Exception as e:
            notification.status = NotificationStatus.FAILED
            notification.error_message = str(e)
            notification.attempts += 1
            logger.error(f"Webhook通知发送失败: {str(e)}")
            return notification.notification_id
    
    async def retry_failed_notifications(self) -> Dict[str, Any]:
        """重试失败的通知."""
        retry_results = {
            "total_retried": 0,
            "successful": 0,
            "failed": 0
        }
        
        notification_ids = self.retry_queue.copy()
        self.retry_queue.clear()
        
        for notification_id in notification_ids:
            notification = next(
                (n for n in self.notification_records if n.notification_id == notification_id),
                None
            )
            
            if not notification or notification.attempts >= notification.max_attempts:
                continue
            
            retry_results["total_retried"] += 1
            
            if notification.notification_type == NotificationType.EMAIL:
                template = self.email_templates.get(notification.template_name)
                if template:
                    success = await self._send_email_notification(notification, template)
                    if success:
                        notification.status = NotificationStatus.SENT
                        notification.sent_at = datetime.now()
                        retry_results["successful"] += 1
                    else:
                        notification.attempts += 1
                        if notification.attempts < notification.max_attempts:
                            self.retry_queue.append(notification_id)
                        retry_results["failed"] += 1
        
        if retry_results["total_retried"] > 0:
            logger.info(f"重试通知完成: {retry_results}")
        
        return retry_results
    
    def get_notification_status(self, notification_id: str) -> Optional[Dict[str, Any]]:
        """获取通知状态."""
        notification = next(
            (n for n in self.notification_records if n.notification_id == notification_id),
            None
        )
        
        if not notification:
            return None
        
        return {
            "notification_id": notification.notification_id,
            "status": notification.status.value,
            "attempts": notification.attempts,
            "created_at": notification.created_at.isoformat(),
            "sent_at": notification.sent_at.isoformat() if notification.sent_at else None,
            "error_message": notification.error_message
        }
    
    def get_notification_statistics(self) -> Dict[str, Any]:
        """获取通知统计信息."""
        total_notifications = len(self.notification_records)
        
        if total_notifications == 0:
            return {
                "total_notifications": 0,
                "sent_count": 0,
                "failed_count": 0,
                "pending_count": 0,
                "success_rate": 0
            }
        
        sent_count = len([n for n in self.notification_records if n.status == NotificationStatus.SENT])
        failed_count = len([n for n in self.notification_records if n.status == NotificationStatus.FAILED])
        pending_count = len([n for n in self.notification_records if n.status == NotificationStatus.PENDING])
        
        success_rate = sent_count / total_notifications if total_notifications > 0 else 0
        
        return {
            "total_notifications": total_notifications,
            "sent_count": sent_count,
            "failed_count": failed_count,
            "pending_count": pending_count,
            "success_rate": success_rate,
            "retry_queue_size": len(self.retry_queue)
        }


# 全局通知管理器实例
_notification_manager = None

def get_notification_manager() -> NotificationManager:
    """获取通知管理器单例."""
    global _notification_manager
    if _notification_manager is None:
        _notification_manager = NotificationManager()
    return _notification_manager